import socket
import threading
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
from tkinter import ttk  # 用于进度条
import struct
import os
import time

MB = 1024 * 1024  # 定义1MB的字节数


class FileSender:
    def __init__(self, master):
        self.master = master
        self.master.title("文件发送端")

        # 接收端 IP 输入
        self.ip_label = tk.Label(master, text="接收端 IP:")
        self.ip_label.pack(pady=5)
        self.ip_entry = tk.Entry(master)
        self.ip_entry.pack(pady=5)

        # 端口输入
        self.port_label = tk.Label(master, text="端口:")
        self.port_label.pack(pady=5)
        self.port_entry = tk.Entry(master)
        self.port_entry.insert(0, "5001")  # 默认端口5001
        self.port_entry.pack(pady=5)

        # 选择文件按钮
        self.select_button = tk.Button(master, text="选择文件", command=self.select_file)
        self.select_button.pack(pady=5)
        self.file_label = tk.Label(master, text="未选择文件")
        self.file_label.pack(pady=5)

        # 发送文件按钮
        self.send_button = tk.Button(master, text="发送文件", command=self.send_file)
        self.send_button.pack(pady=5)

        # 进度条
        self.progress = ttk.Progressbar(master, orient="horizontal", length=300, mode="determinate")
        self.progress.pack(pady=5)

        # 速度显示（以 MB/s 显示）
        self.speed_label = tk.Label(master, text="速度: 0.00 MB/s")
        self.speed_label.pack(pady=5)

        # 日志显示区域
        self.log = scrolledtext.ScrolledText(master, width=60, height=10)
        self.log.pack(padx=10, pady=10)

        self.file_path = None

    def select_file(self):
        path = filedialog.askopenfilename(title="选择要发送的文件")
        if path:
            self.file_path = path
            self.file_label.config(text=os.path.basename(path))
            self.log_message("选中文件: {}".format(path))
        else:
            self.log_message("未选择文件")

    def send_file(self):
        if not self.file_path:
            messagebox.showerror("错误", "请先选择文件")
            return
        ip = self.ip_entry.get().strip()
        if not ip:
            messagebox.showerror("错误", "请输入接收端 IP 地址")
            return
        try:
            port = int(self.port_entry.get().strip())
        except ValueError:
            messagebox.showerror("错误", "端口号必须为数字")
            return

        # 开启新线程发送文件，避免阻塞 UI
        threading.Thread(target=self.handle_send, args=(ip, port), daemon=True).start()

    def handle_send(self, ip, port):
        try:
            file_name = os.path.basename(self.file_path)
            file_size = os.path.getsize(self.file_path)
            self.log_message("正在连接到 {}:{} ...".format(ip, port))
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect((ip, port))
            self.log_message("连接成功！")

            # 发送文件名长度和文件名
            fname_bytes = file_name.encode("utf-8")
            sock.sendall(struct.pack("!I", len(fname_bytes)))
            sock.sendall(fname_bytes)

            # 发送文件大小（8字节）
            sock.sendall(struct.pack("!Q", file_size))
            self.log_message("开始发送文件：{} （{} 字节）".format(file_name, file_size))

            # 在主线程设置进度条最大值
            self.master.after(0, lambda: self.progress.config(maximum=file_size))

            sent = 0
            start_time = time.time()
            update_interval = 0.5  # 每0.5秒更新一次UI
            last_update_time = time.time()

            with open(self.file_path, "rb") as f:
                while True:
                    chunk = f.read(4096)
                    if not chunk:
                        break
                    sock.sendall(chunk)
                    sent += len(chunk)

                    current_time = time.time()
                    # 每隔 update_interval 秒更新一次进度条和速度
                    if current_time - last_update_time >= update_interval:
                        elapsed = current_time - start_time
                        speed = sent / elapsed if elapsed > 0 else sent
                        # 在主线程更新
                        self.master.after(0, lambda s=sent: self.progress.config(value=s))
                        self.master.after(
                            0,
                            lambda sp=speed: self.speed_label.config(text="速度: {:.2f} MB/s".format(sp / MB))
                        )
                        last_update_time = current_time

            # 最后一次更新（确保UI显示最终状态）
            elapsed = time.time() - start_time
            speed = sent / elapsed if elapsed > 0 else sent
            self.master.after(0, lambda s=sent: self.progress.config(value=s))
            self.master.after(
                0,
                lambda sp=speed: self.speed_label.config(text="速度: {:.2f} MB/s".format(sp / MB))
            )
            self.log_message("文件发送完毕！")
        except Exception as e:
            self.log_message("发送文件时发生错误: {}".format(str(e)))
        finally:
            sock.close()

    def log_message(self, msg):
        self.log.insert(tk.END, msg + "\n")
        self.log.see(tk.END)


if __name__ == "__main__":
    root = tk.Tk()
    app = FileSender(root)
    root.mainloop()
